﻿using System;
using BuildStatusIndicator.Controller.EventBroker;
using BuildStatusIndicator.Controller.Events;
using BuildStatusIndicator.Controller.TfsConnectionStatergy;
using Microsoft.TeamFoundation.Build.Client;

namespace BuildStatusIndicator.Controller
{
    public class TfsController : IController
    {
        private readonly IBuildStatusEventPublisher _eventPublisher;
        private IQueuedBuildsView _buildQueue;
        private IBuildServer _buildServer;
        private IConnectinonStratergy _stratergy;
        private bool _connected = false;


        public TfsController(IBuildStatusEventPublisher eventPublisher, IConnectinonStratergy stratergy)
        {
            _eventPublisher = eventPublisher;
            _stratergy = stratergy;
        }

        #region IController Members

        public void Dispose()
        {
            if (_connected)
                Disconnect();
        }

        public void Disconnect()
        {
            _buildQueue.StatusChanged -= BuildStatusPolled;
            _buildQueue.Disconnect();
            _eventPublisher.PublishEvent(new BuildNotificationEndingEvent());
            _connected = false;
        }

        #endregion

        public void Connect()
        {
            _buildServer = _stratergy.GetBuildServer();

            _buildQueue = _buildServer.CreateQueuedBuildsView(_stratergy.TeamProject);

            _buildQueue.StatusFilter = QueueStatus.Completed | QueueStatus.InProgress;

            CheckStartStatus(_stratergy.TeamProject);

            _buildQueue.StatusChanged += BuildStatusPolled;

            _buildQueue.Connect((int) _stratergy.PollingIntervall, null);

            _eventPublisher.PublishEvent(new BuildNotificationStartingEvent());
            
            _connected = true;
        }

        private void CheckStartStatus(string teamProject)
        {
            if (HasBuilds(GetTeamProjectBuilds(teamProject)))
            {
                IBuildDetail firstSuccessfullBuildDetail = null;

                foreach (IBuildDetail detail in GetTeamProjectBuilds(teamProject).Builds)
                {
                    if (IsNonSuccessfullBuild(detail))
                    {
                        PublishBuildStatusEvent(detail);
                        return;
                    }

                    if (IsSuccessfullBuild(detail) && firstSuccessfullBuildDetail == null)
                        firstSuccessfullBuildDetail = detail;
                }

                if (firstSuccessfullBuildDetail != null)
                    PublishBuildStatusEvent(firstSuccessfullBuildDetail);
            }
        }

        private static bool IsSuccessfullBuild(IBuildDetail detail)
        {
            return detail.Status == BuildStatus.Succeeded;
        }

        private static bool IsNonSuccessfullBuild(IBuildDetail detail)
        {
            return detail.Status == BuildStatus.InProgress ||
                   detail.Status == BuildStatus.Failed ||
                   detail.Status == BuildStatus.PartiallySucceeded;
        }

        private IBuildQueryResult GetTeamProjectBuilds(string teamProject)
        {
            IBuildDetailSpec buildDetailSpec = _buildServer.CreateBuildDetailSpec(teamProject);
            buildDetailSpec.MaxBuildsPerDefinition = 1;
            buildDetailSpec.QueryOrder = BuildQueryOrder.FinishTimeDescending;

            return _buildServer.QueryBuilds(buildDetailSpec);
        }

        private static bool HasBuilds(IBuildQueryResult results)
        {
            return results.Builds.Length > 0;
        }


        private void BuildStatusPolled(object sender, StatusChangedEventArgs e)
        {
            _eventPublisher.PublishEvent(new BuildStatusPolledEvent(){PollTime = DateTime.Now});

            if (e.Changed && _buildQueue.QueuedBuilds.Length > 0)
            {
                int lastItem = _buildQueue.QueuedBuilds.Length - 1;

                IBuildDetail detail = _buildQueue.QueuedBuilds[lastItem].Build;
                PublishBuildStatusEvent(detail);
            }
        }

        private void PublishBuildStatusEvent(IBuildDetail detail)
        {
            switch (detail.Status)
            {
                case BuildStatus.Failed:
                    _eventPublisher.PublishEvent(new BuildFailedEvent(detail));
                    break;
                case BuildStatus.InProgress:
                    _eventPublisher.PublishEvent(new BuildInProgressEvent(detail));
                    break;
                case BuildStatus.NotStarted:
                    _eventPublisher.PublishEvent(new BuildNotStartedEvent(detail));
                    break;
                case BuildStatus.PartiallySucceeded:
                    _eventPublisher.PublishEvent(new BuildPartiallySucceededEvent(detail));
                    break;
                case BuildStatus.Stopped:
                    _eventPublisher.PublishEvent(new BuildStoppedEvent(detail));
                    break;
                case BuildStatus.Succeeded:
                    _eventPublisher.PublishEvent(new BuildSucceededEvent(detail));
                    break;
                default:
                    break;
            }
        }
    }
}